package org.luxor.commons.storage.client;

import org.luxor.commons.storage.exception.FastDfsTokenInvalidException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.NoSuchAlgorithmException;
import java.time.Instant;

/**
 * 防盗链Token生成
 *
 * @author Mr.yan @date 2019/12/17
 */
public class AntiStealToken {
    private static final Logger logger = LoggerFactory.getLogger(AntiStealToken.class);

    /**
     * 生成防盗链令牌
     *
     * @param filePath  文件路径
     * @param secretKey 私钥
     * @param charse    字符集
     * @param ts        令牌生成时间(秒)
     * @return fastDfs防盗链凭证
     */
    public static String getToken(String filePath, String secretKey, String charse, long ts) throws FastDfsTokenInvalidException {
        try {
            byte[] bsFilename = filePath.getBytes(charse);
            byte[] bsKey = secretKey.getBytes(charse);
            byte[] bsTimestamp = (new Long(ts)).toString().getBytes(charse);

            byte[] buff = new byte[bsFilename.length + bsKey.length + bsTimestamp.length];
            System.arraycopy(bsFilename, 0, buff, 0, bsFilename.length);
            System.arraycopy(bsKey, 0, buff, bsFilename.length, bsKey.length);
            System.arraycopy(bsTimestamp, 0, buff, bsFilename.length + bsKey.length, bsTimestamp.length);
            return md5(buff);
        } catch (Exception e) {
            logger.error("FastDfs 生成防盗链TOKEN失败", e);
            throw new FastDfsTokenInvalidException("FastDfs 生成防盗链TOKEN失败", e);
        }
    }

    /**
     * 检查防盗链令牌的准确性
     *
     * @param srcToken     待验证的令牌
     * @param ttl          令牌有效期(单位:秒)
     * @param fileFullPath 文件路径
     * @param secretKey    私钥
     * @param charse       字符集
     * @param ts           待验证的令牌生成时间
     * @return true 表示验证通过，false 表示令牌无效
     */
    public static boolean verify(String srcToken, long ttl, String fileFullPath, String secretKey, String charse, Long ts) {
        if (srcToken == null || ts == null) {
            return false;
        }
        long cts = Instant.now().getEpochSecond();
        if (cts > (ts + ttl)) {
            return false;
        }
        if (cts < ts) {
            return false;
        }
        long timePoint = cts - (cts - ts);
        String token = getToken(fileFullPath, secretKey, charse, timePoint);
        if (token.equals(srcToken)) {
            return true;
        }
        return false;
    }

    private static char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    private static String md5(byte[] source) throws NoSuchAlgorithmException {
        java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
        md.update(source);
        byte[] tmp = md.digest();
        char[] str = new char[32];
        int k = 0;
        for (int i = 0; i < 16; i++) {
            str[k++] = hexDigits[tmp[i] >>> 4 & 0xf];
            str[k++] = hexDigits[tmp[i] & 0xf];
        }
        return new String(str);
    }

}
